package com.conncui.common.refelect;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;

/**
 * The type Reflect.
 */
public class Reflect<T> {
    public static final String TAG = "Reflect";
    /**
     * The M class.
     */
    Class<T> mClass;
    /**
     * The M method.
     */
    Method mMethod;
    /**
     * The M method arr.
     */
    Method[] mMethodArr;
    Method[] mDeclaredMethodArr;

    Field[] mFields;

    String mClassPath;

    public Reflect(String classPath) {
        mClassPath = classPath;
        try {
            mClass = (Class<T>) Class.forName(mClassPath);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
        }
    }

    /**
     * Instantiates a new Reflect.
     *
     * @param aClass the a class
     */
    public Reflect(Class aClass) {
        mClass = (Class<T>) aClass;
    }

    /**
     * Instantiates a new Reflect.
     *
     * @param object the objects
     */
    public Reflect(Object object) {
        mClass = (Class<T>) object.getClass();
    }

    public Class<T> getTargetClass() {
        return mClass;
    }

    /**
     * Instance method method.
     *
     * @param methodName     the method name
     * @param parameterTypes the parameter types
     *
     * @return the method
     */
    public Method getMethod(String methodName, Class<?>... parameterTypes) {
        Method method = null;
        try {
            method = mClass.getMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } finally {
        }
        return method;
    }

    public Method[] getMethods() {
        if (mMethodArr == null) {
            mMethodArr = mClass.getMethods();
        }
        return mMethodArr;
    }

    public Method[] getDeclaredMethods() {
        if (mDeclaredMethodArr == null) {
            mDeclaredMethodArr = mClass.getDeclaredMethods();
        }
        return mDeclaredMethodArr;
    }

    /**
     * Instance method method.
     *
     * @param methodName     the method name
     * @param parameterTypes the parameter types
     *
     * @return the method
     */
    public Method getSuperMethod(String methodName, Class<?>... parameterTypes) {
        Method method = null;
        try {
            method = mClass.getDeclaredMethod(methodName, parameterTypes);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } finally {
        }
        return method;
    }

    public Method getSuperMethod2(Object object, String methodName, Class<?>... parameterTypes) {
        Method method = null;
        for (Class<?> clazz = object.getClass().getSuperclass(); clazz != Object.class;
             clazz = clazz.getSuperclass()) {
            System.out.println(TAG + "," + clazz.getSimpleName());
            try {
                method = clazz.getDeclaredMethod(methodName, parameterTypes);
                System.out.println(TAG + "," + method.getName());
                return method;
            } catch (Exception e) {
            }
        }
        return null;
    }

    /**
     * Call object.
     *
     * @param receiver the receiver
     * @param method   the method
     * @param args     the args
     *
     * @return the object
     */
    public Object invoke(Object receiver, Method method, Object... args) {
        Object result = null;
        try {
            result = method.invoke(receiver, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } finally {
        }
        return result;
    }

    public Class getReturnType(Method method) {
        return method.getReturnType();
    }

    public Field[] getFields() {
        return mClass.getFields();
    }

    public Field[] getDeclaredFields() {

        return mClass.getDeclaredFields();

    }

    public Field getInheritField(Object object, String fieldName) {
        return getInheritField(object.getClass(), fieldName);
    }

    public Field getInheritField(Class objectClass, String fieldName) {
        Class currentClass = objectClass;
        Field field = null;
        do {
            try {
                field = currentClass.getDeclaredField(fieldName);
                field.setAccessible(true);
            } catch (Exception e) {

            }
            if (field != null) {
                break;
            }
            currentClass = currentClass.getSuperclass();

        } while (currentClass != null);

        return field;
    }

    public Map<String, Field> getFieldKVMap(Class objectClass) {
        Map<String, Field> result = new HashMap<>();
        // 包括父类字段
        //        Field[] fields = objectClass.getFields();
        // 不包括父类字段
        Field[] fields = this.getDeclaredFields();

        for (Field field : fields) {
            field.setAccessible(true);
            result.put(field.getName(), field);
        }
        return result;
    }

    public Map<String, Object> getAllFieldKVMap(Object object) {
        Map<String, Object> result = new HashMap<>();
        Class currentClass = null;
        do {
            result.putAll(getFieldKVMap(currentClass));
            currentClass = currentClass.getSuperclass();
        } while (currentClass != null);
        return result;
    }

    public Map<String, Object> getFieldValueMap(Object object) {
        Map<String, Object> result = new HashMap<>();
        // 包括父类字段
        Field[] fields = this.getFields();
        // 不包括父类字段
        //        Field[] fields = this.getDeclaredFields();

        try {
            for (Field field : fields) {
                field.setAccessible(true);
                result.put(field.getName(), field.get(object));
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } finally {
        }
        return result;
    }

    public Map<String, Object> getStaticFieldKVMap() {
        Map<String, Object> result = new HashMap<>();
        Field[] fields = this.getDeclaredFields();
        try {
            for (Field field : fields) {
                if (Modifier.isStatic(field.getModifiers())) {
                    field.setAccessible(true);
                    result.put(field.getName(), field.get(null));
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } finally {
        }
        return result;
    }

    public Class[] getMethodParameter(Method method) {
        Class<?>[] parameterTypeArr = method.getParameterTypes();
        for (int i = 0; i < parameterTypeArr.length; i++) {
            //            ToolLog.d(TAG,parameterTypeArr[i].toString());
            //            ToolLog.d(TAG,parameterTypeArr[i].getName());
            System.out.println(TAG + "," + method.getName());
            System.out.println(TAG + "," + parameterTypeArr[i].getSimpleName());
        }
        return parameterTypeArr;
    }

    public static int getModifier(Field field) {
        return field.getModifiers();
    }

    public static int getModifier(Method method) {
        return method.getModifiers();
    }
}

